嵌套路由的需求场景
课程详情页和项目详情页共享相同的页面框架:顶部轮播图、导航搜索栏、底部 Footer,只有中间内容区域不同。这种场景需要使用**嵌套路由(Nested Routes)**来实现。
页面结构分析:
+----------------------------------+
| Swiper 轮播头部 |
| 导航 + 搜索 + 登录 |
+----------------------------------+
| |
| 课程介绍(左图右文) |
| |
+----------------------------------+
| Tab 导航 |
| [课程介绍] [章节目录] [学员评价] |
| +--------------------+--------+ |
| | | | |
| | <RouterView /> | 侧边栏 | |
| | (Markdown 内容) | | |
| | | | |
| +--------------------+--------+ |
+----------------------------------+
text
vite-plugin-pages 的嵌套路由实现
vite-plugin-pages 官方文档中提供了 nested routes 的实现方式。关键规则:
在某个路由目录下,如果同时存在
folder-name/目录和同级的folder-name.vue文件,则folder-name.vue作为外层布局容器(包含公共内容),folder-name/目录下的文件作为子路由内容。
文件结构
src/pages/
study.vue # 外层布局(包含公共组件 + <RouterView>)
study/
test.md # 子路由页面(Markdown 内容)
index.vue # 默认子页面(可选)
text
外层布局 study.vue
<script setup lang="ts">
import { ref } from 'vue'
import { useThemeStore } from '~/store/useThemeStore'
const themeStore = useThemeStore()
</script>
<template>
<div class="w-full">
<!-- 头部区域:轮播 + 导航 -->
<div>
<Swiper :items="swiperItems" />
<div><!-- 搜索 + 登录 --></div>
</div>
<!-- 内容区域 -->
<Container>
<!-- 课程介绍:左图右文 -->
<div class="flex">
<div class="w-1/2">
<img :src="courseImage" class="w-full" />
</div>
<div class="w-1/2 p-8">
<h2 class="text-3xl">{{ courseName }}</h2>
<p class="text-gray-400">{{ courseDesc }}</p>
<div class="flex text-sm text-gray-400 mt-4 mb-6">
<span>toimc</span>
<span class="mx-2">|</span>
<span>前端大牛</span>
</div>
<div class="text-orange-400 text-2xl mb-6">
¥399.00
</div>
<div class="flex gap-4">
<button class="bg-sky-400 text-white px-4 py-2
hover:bg-sky-300">
加入购物车
</button>
<button class="border border-sky-400 text-sky-400 px-8 py-2
hover:bg-gray-300 hover:text-sky-500">
收藏
</button>
</div>
</div>
</div>
<!-- Tab 区域 + 内容 + 侧边栏 -->
<div class="flex">
<!-- 左侧:Tab + RouterView -->
<div class="w-3/4">
<!-- Tab 导航 -->
<ul class="flex">
<li class="text-sky-400">课程介绍</li>
<li>章节目录</li>
<li>学员评价</li>
</ul>
<!-- 子路由渲染位置 -->
<RouterView />
</div>
<!-- 右侧:侧边栏 -->
<div class="w-1/4">侧边栏</div>
</div>
</Container>
</div>
</template>
vue
子路由页面 test.md
# 课程详情
这里放置 Markdown 格式的课程详细内容...
<ImageSwiper
:items="items"
:titles="titles"
:height="420"
/>
markdown
访问 /study/test 时,study.vue 作为外层布局,test.md 的内容渲染到 <RouterView /> 的位置。
课程介绍区域的样式实现
左图右文布局
使用 Flex 布局实现左右各占 50%:
<div class="flex">
<div class="w-1/2"><!-- 图片 --></div>
<div class="w-1/2 p-8"><!-- 文字信息 --></div>
</div>
html
按钮样式规范
建议将按钮样式提取为全局 CSS 类,方便复用:
| 按钮 | 样式 |
|---|---|
| 加入购物车 | bg-sky-400 text-white px-4 py-2 hover:bg-sky-300 |
| 收藏 | border border-sky-400 text-sky-400 px-8 py-2 hover:bg-gray-300 hover:text-sky-500 |
Tab 导航基础结构
<ul class="flex w-full">
<li class="text-sky-400 cursor-pointer">课程介绍</li>
<li class="cursor-pointer">章节目录</li>
<li class="cursor-pointer">学员评价</li>
</ul>
html
后续可通过 activeIndex 控制 Tab 高亮和内容切换。
关键注意事项
RouterView 的位置
在 study.vue 中必须包含 <RouterView />,这是子路由渲染的出口。没有这个标签,子路由页面将无法显示。
Container 组件的使用
Container 组件负责约束内容宽度,需要合理嵌套:
<Container>
<!-- 所有需要限定宽度的内容 -->
</Container>
html
Markdown 组件高度适配
在 Markdown 中使用的组件(如 ImageSwiper),其高度参数需要根据外层布局合理设置,推荐 400-450px 左右。
嵌套层级与状态管理
详情页的状态(课程信息、Tab 切换等)应放在 study.vue 外层布局中管理,避免子路由与外层布局之间的复杂传参。
知识点总结
| 概念 | 说明 |
|---|---|
| 嵌套路由 | pages/study.vue + pages/study/*.md 实现 |
<RouterView /> | 子路由渲染的出口,必须存在于外层布局 |
| Flex 布局 | 左图右文、Tab + 内容 + 侧边栏等区域划分 |
| Container | 约束内容宽度,保持视觉一致性 |
| Markdown + Vue 混用 | 中间内容区域使用 Markdown,外围使用 Vue 组件 |
↑